﻿using Jupiter.Infrastructure.Cache.Interfaces;
using Jupiter.Infrastructure.Domain;
using Jupiter.Infrastructure.Domain.SqlSugar;
using Jupiter.Infrastructure.Domain.SqlSugar.Basic;
using Jupiter.Infrastructure.Exceptions;
using Jupiter.Infrastructure.Mappings;
using Jupiter.Infrastructure.Permission;
using Jupiter.Infrastructure.Permission.DataPermissions;
using MediatR;
using Microsoft.Extensions.Configuration;
using Wips.AppService.Publisher.PublishFlows.Requests;
using Wips.Domain.Events;
using Wips.Domain.Models;
using Wips.Infrastructure.Model;

namespace Wips.AppService.SqlSugar.Publisher
{
    /// <summary>
    /// 发布
    /// </summary>
    public class PublishFlowRequestHandler : RequestHandlerBase<PublishFlow>,
        IRequestHandler<StartPublishRequest, string>,
        IRequestHandler<SavePublishFlowNodeRequest, string>,
        IRequestHandler<SurePublishRequest, string>,
        IRequestHandler<CommandRunScriptRequest, List<string>>,
        IRequestHandler<CancelPublishRequest, string>,
        IRequestHandler<RestorePublishRequest, string>,
        IRequestHandler<CompletePublishRequest, string>,
        INotificationHandler<AddPublishLogEvent>
    {
        /// <summary>
        /// 
        /// </summary>
        public readonly IMapsterMapper _mapsterMapper;
        private readonly PublishMasterService _masterService;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="dbContext"></param>
        /// <param name="securityContextAccessor"></param>
        /// <param name="facadeResolver"></param>
        /// <param name="cacheManager"></param>
        /// <param name="configuration"></param>
        /// <param name="dataPermission"></param>
        public PublishFlowRequestHandler(ISqlSugarClient dbContext,
            ISecurityContextAccessor securityContextAccessor,
            IFacadeResolver facadeResolver, ICacheManager cacheManager,
            IConfiguration configuration, IDataPermission? dataPermission, IMapsterMapper mapsterMapper, PublishMasterService masterService) :
            base(dbContext, securityContextAccessor, facadeResolver, cacheManager, configuration, dataPermission)
        {
            _mapsterMapper = mapsterMapper;
            _masterService = masterService;
        }

        /// <summary>
        /// 开始发布
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<string> Handle(StartPublishRequest request, CancellationToken cancellationToken)
        {
            var serverAgent = await Query<AgentServerConfig>()
                .Where(c => c.Id == request.AgentServerConfigId)
                .FirstAsync();
            Check.Validate<CoreException>(serverAgent == null, "未找到子服务器服务");

            var item = _mapsterMapper.MapTo<PublishFlow>(request);
            item.PublishFlowStatus = EnumPublishFlowStatus.Processing;
            await RegisterNewAsync(item);

            return item.Id!;

        }

        /// <summary>
        /// 保存节点结果
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="CoreException"></exception>
        public async Task<string> Handle(SavePublishFlowNodeRequest request, CancellationToken cancellationToken)
        {

            var flow = await Query<PublishFlow>()
                .Where(c => c.Id == request.PublishFlowId)
                .FirstAsync();
            Jupiter.Infrastructure.Check.Validate<CoreException>(flow == null, "未找到发布流程");
            var item = new PublishFlowNode();

            item.PublishFlowId = request.PublishFlowId;
            item.Result = request.Result;
            item.NodeType = request.NodeType;
            item.NodeStatus = EnumNodeStatus.Success;

            if (request.NodeType == EnumNodeType.BackUp)
            {
                await RegisterNewObjectValueAsync(item);
            }
            else
            {
                // 需要判断上一个节点是否成功
                var lastNode = await Query<PublishFlowNode>()
                    .Where(c => c.PublishFlowId == request.PublishFlowId)
                    .Where(c => (int)c.NodeType == ((int)request.NodeType - 1))
                    .FirstAsync();
                if (lastNode.NodeStatus == EnumNodeStatus.Success)
                {
                    await RegisterNewObjectValueAsync(item);
                }
                else
                {
                    throw new CoreException("上一个未成功！请刷新页面重试");
                }

            }

            return item.Id!;
        }
        /// <summary>
        /// 执行发布合并
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="CoreException"></exception>
        public async Task<string> Handle(SurePublishRequest request, CancellationToken cancellationToken)
        {

            var flow = await Query()
              .Includes(c => c.AgentServer)
              .Includes(c => c.AgentServerConfig)
              .Includes(c => c.Nodes)
              .Where(c => c.Id == request.Id).FirstOrDefaultAsync();
            if (flow == null)
            {
                throw new CoreException("未找到对应发布流程数据！");
            }
            try
            {
                var node = flow.Nodes!.Where(c => c.NodeType == EnumNodeType.Upload).FirstOrDefault();
                var push = new PublishRequest(request.Id, UserId)
                {
                    Id = request.Id,
                    RootPath = flow.AgentServerConfig!.RootPath,
                    ZipPath = node!.Result
                };

                if (request.IsAutoStopReStart)
                {
                    var stopCommand = @$"%systemroot%\system32\inetsrv\appcmd stop site /site.name:""{flow.AgentServerConfig.IISName}""";
                    if (string.IsNullOrEmpty(flow.AgentServerConfig.IISName))
                    {
                        throw new CoreException("请先去配置iis站点名称");
                    }
                    var stop = await _masterService.RunScriptAsync(flow.AgentServer!.AgentAddress, new RunScriptRequest(request.Id, UserId)
                    {
                        Command = stopCommand
                    });
                    if (!stop.Success)
                    {
                        throw new CoreException(stop.Message);
                    }
                }
                var result = await _masterService.PublishAsync(flow.AgentServer!.AgentAddress, push);
                if (result.Success)
                {
                    var item = new PublishFlowNode();

                    item.PublishFlowId = request.Id;
                    item.Result = "";
                    item.NodeType = EnumNodeType.Publish;
                    item.NodeStatus = EnumNodeStatus.Success;
                    await RegisterNewObjectValueAsync(item);


                    if (request.IsAutoStopReStart)
                    {
                        var startCommand = @$"%systemroot%\system32\inetsrv\appcmd start site /site.name:""{flow.AgentServerConfig.IISName}""";
                        var start = await _masterService.RunScriptAsync(flow.AgentServer!.AgentAddress, new RunScriptRequest(request.Id, UserId)
                        {
                            Command = startCommand
                        });
                        if (!start.Success)
                        {
                            throw new CoreException(start.Message);
                        }
                    }
                    return result.Data!;
                }
                else
                {
                    throw new CoreException(result.Message);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw new CoreException(ex.Message);
            }

        }

        /// <summary>
        /// 命令执行脚本
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<List<string>> Handle(CommandRunScriptRequest request, CancellationToken cancellationToken)
        {

            var flow = await Query()
              .Includes(c => c.AgentServer)
              .Includes(c => c.AgentServerConfig)
              .Includes(c => c.Nodes)
              .Where(c => c.Id == request.Id).FirstOrDefaultAsync();
            if (flow == null)
            {
                throw new CoreException("未找到对应发布流程数据！");
            }
            try
            {
                var commandList = request.Command.Split(';');
                var resultList = new List<string>();
                foreach (var command in commandList)
                {
                    if (!string.IsNullOrWhiteSpace(command))
                    {
                        var result = await _masterService.RunScriptAsync(flow.AgentServer!.AgentAddress, new RunScriptRequest(request.Id, UserId)
                        {
                            Command = command
                        });
                        resultList.Add(result.Data!);
                    }

                }
                return resultList;

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                throw new CoreException(ex.Message);
            }
        }

        /// <summary>
        /// 保存日志
        /// </summary>
        /// <param name="notification"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task Handle(AddPublishLogEvent notification, CancellationToken cancellationToken)
        {

            var item = new PublishLog(notification);



            await RegisterNewObjectValueAsync(item);
        }

        /// <summary>
        /// 执行还原
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="CoreException"></exception>
        public async Task<string> Handle(RestorePublishRequest request, CancellationToken cancellationToken)
        {
            var item = await Query()
                .Includes(c => c.Nodes)
                .Includes(c => c.AgentServerConfig)
             .Includes(c => c.AgentServer)
                .Where(c => c.Id == request.Id)
                .FirstAsync();
            Check.Validate<CoreException>(item == null, "未找到发布流程");
            //Check.Validate<CoreException>(item!.PublishFlowStatus == EnumPublishFlowStatus.Finish, "发布流程已完成，无法还原");
            Check.Validate<CoreException>(item.Nodes == null || !item.Nodes.Any(c => c.NodeType == EnumNodeType.BackUp), "未备份，无法还原");
            var backUpNode = item.Nodes!.Where(c => c.NodeType == EnumNodeType.BackUp).FirstOrDefault();

            var result = await _masterService.RestoreAsync(item.AgentServer!.AgentAddress, new PublishRequest(request.Id, UserId)
            {
                ZipPath = backUpNode!.Result,
                RootPath = item.AgentServerConfig!.RootPath,
                Id = request.Id
            });
            if (result.Success)
            {
                item.PublishFlowStatus = EnumPublishFlowStatus.ReStore;
                RegisterDirty(item);
            }
            else
            {
                throw new CoreException(result.Message);
            }

            return item.Id!;

        }

        /// <summary>
        /// 取消/作废发布流程
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<string> Handle(CancelPublishRequest request, CancellationToken cancellationToken)
        {
            var item = await Query()
               .Where(c => c.Id == request.Id)
               .FirstAsync();

            Check.Validate<CoreException>(item!.PublishFlowStatus != EnumPublishFlowStatus.Processing, "只有进行中的流程可以作废！");
            item.PublishFlowStatus = EnumPublishFlowStatus.Cancel;
            RegisterDirty(item);
            return item.Id!;
        }

        /// <summary>
        /// 完成发布流程
        /// </summary>
        /// <param name="request"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        /// <exception cref="NotImplementedException"></exception>
        public async Task<string> Handle(CompletePublishRequest request, CancellationToken cancellationToken)
        {
            var item = await Query()
                .Includes(c => c.Nodes)
                .Where(c => c.Id == request.Id)
                .FirstAsync();

            Check.Validate<CoreException>(item!.PublishFlowStatus != EnumPublishFlowStatus.Processing, "只有进行中的流程可以完成！");
            Check.Validate<CoreException>(item.Nodes == null || item.Nodes.Count < 3, "请至少完成备份，上传，发布流程，才可以完成");
            item.PublishFlowStatus = EnumPublishFlowStatus.Finish;
            RegisterDirty(item);
            return item.Id!;
        }
    }
}
